Warm-up exercises from tutorial
- Choose 2 graphs you have created for ANY assignment in this class and add interactivity using the
ggplotly() function.
ratings <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-01-25/ratings.csv')
details <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-01-25/details.csv')
theme_set(theme_minimal())
boardgame_haven <- ratings %>%
left_join(details,
by = c("id" = "id"))
single_parent_only_child_household_games_lol <- boardgame_haven %>%
filter(maxplayers == 2) %>%
arrange(rank) %>%
slice_head(n = 10) %>%
ggplot() +
geom_col(aes(x = users_rated,
y = fct_reorder(name, users_rated, .desc = FALSE),
fill = name,
text = name)) +
labs(title = "Top 10 Games for Two-Players",
subtitle = "This graph shows the number of users who rated each game.",
x = "",
y = "") +
theme(legend.position = "none",
axis.text.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank())
data(penguins)
penguins <- penguins %>%
mutate(AngryBird = mean(bill_length_mm, na.rm = TRUE))
waddles <- penguins %>%
ggplot(aes(x=bill_length_mm, text = AngryBird))+
geom_histogram(binwidth= 2, fill="purple4") +
geom_vline(xintercept=mean(penguins$bill_length_mm, na.rm = TRUE), linetype = "dashed", color = "red") + #na.rm=TRUE to remove the na lines
labs(title="These Bills Do Not Pay But They're Great Anyway",
x = "Bill Length (mm)",
y = "Number of Penguins")
ggplotly(single_parent_only_child_household_games_lol,
tooltip = c("text", "x"))
ggplotly(waddles,
tooltip = c("text", "x"))
- Use animation to tell an interesting story with the
small_trains dataset that contains data from the SNCF (National Society of French Railways). These are Tidy Tuesday data! Read more about it here.
small_trains %>%
filter(service == "International") %>%
ggplot(aes(x = delayed_number,
y = delay_cause,
group = year)) +
geom_jitter() +
labs(title = "International Train Service in France and Causes for Delay",
subtitle = "Year: {frame_time}",
y = "",
x = "Percent of trains delayed") +
transition_time(year)

anim_save("Q2frenchtrains.gif")
knitr::include_graphics("Q2frenchtrains.gif")

Garden data
- In this exercise, you will create a stacked area plot that reveals itself over time (see the
geom_area() examples here). You will look at cumulative harvest of tomato varieties over time. You should do the following:
- From the
garden_harvest data, filter the data to the tomatoes and find the daily harvest in pounds for each variety.
- Then, for each variety, find the cumulative harvest in pounds.
- Use the data you just made to create a static cumulative harvest area plot, with the areas filled with different colors for each variety and arranged (HINT:
fct_reorder()) from most to least harvested weights (most on the bottom).
- Add animation to reveal the plot over date.
I have started the code for you below. The complete() function creates a row for all unique date/variety combinations. If a variety is not harvested on one of the harvest dates in the dataset, it is filled with a value of 0.
daily_tomato <- garden_harvest %>%
filter(vegetable == "tomatoes") %>%
group_by(date, variety) %>%
summarize(daily_harvest_lb = sum(weight)*0.00220462) %>%
ungroup() %>%
complete(variety,
date,
fill = list(daily_harvest_lb = 0)) %>%
mutate(variety = fct_reorder(variety, daily_harvest_lb, sum)) %>%
group_by(variety) %>%
mutate(cumsum_tomato = cumsum(daily_harvest_lb)) #1. which variable do you want to reorder, 2. by which variable, function you want to use?
daily_tomato %>%
ggplot(aes(x = date, y = cumsum_tomato, fill = variety)) +
geom_area() +
geom_text(aes(label = variety), position = "stack") +
labs(title = "Tomato Time: Cumulative Harvest in Pounds Over Time",
subtitle = "Date: {frame_along}",
x = "",
y = "") +
theme(legend.position = "none") +
transition_reveal(date)

anim_save("Q3tomatoharvest.gif")
#transition date only shows what happens on a specific date; transition reveal shows progression
knitr::include_graphics("Q3tomatoharvest.gif")
## Maps, animation, and movement!
- Map Lisa’s
mallorca_bike_day7 bike ride using animation! Requirements:
- Plot on a map using
ggmap.
- Show “current” location with a red point.
- Show path up until the current point.
- Color the path according to elevation.
- Show the time in the subtitle.
- CHALLENGE: use the
ggimage package and geom_image to add a bike image instead of a red point. You can use this image. See here for an example.
- Add something of your own! And comment on if you prefer this to the static map and why or why not.
mallorca_map <- get_stamenmap(
bbox = c( left = 2.1629, bottom = 39.2248, right = 3.6598, top = 40.0013),
maptype = "terrain",
zoom = 11)
ggmap(mallorca_map) +
geom_path(data = mallorca_bike_day7,
aes(x = lon,
y = lat,
color = ele),
size = 1) +
geom_point(data = mallorca_bike_day7,
aes(x = lon,
y = lat),
size = 2,
color = "red") +
scale_color_viridis_c(option = "magma") +
theme_map() +
labs(title = "Lisa's Mallorca Bike Ride: More Elevated Than Ever Before",
subtitle = "Date: {frame_along}",
x = "",
y = "") +
theme(legend.background = element_blank()) +
transition_reveal(time)

anim_save("Q4mallorcabike.gif")
knitr::include_graphics("Q4mallorcabike.gif")
5. In this exercise, you get to meet Lisa’s sister, Heather! She is a proud Mac grad, currently works as a Data Scientist where she uses R everyday, and for a few years (while still holding a full-time job) she was a pro triathlete. You are going to map one of her races. The data from each discipline of the Ironman 70.3 Pan Am championships, Panama is in a separate file - panama_swim, panama_bike, and panama_run. Create a similar map to the one you created with my cycling data. You will need to make some small changes: 1. combine the files putting them in swim, bike, run order (HINT: bind_rows()), 2. make the leading dot a different color depending on the event (for an extra challenge, make it a different image using `geom_image()!), 3. CHALLENGE (optional): color by speed, which you will need to compute on your own from the data. You can read Heather’s race report here. She is also in the Macalester Athletics Hall of Fame and still has records at the pool.
panama_map <- get_stamenmap(
bbox = c( left = -79.6866, bottom = 8.8254, right = -79.4817, top = 8.9966),
maptype = "terrain",
zoom = 11)
panama_heather <- bind_rows(list(panama_swim, panama_bike, panama_run))
ggmap(panama_map) +
geom_path(data = panama_heather,
aes(x = lon,
y = lat,
color = event),
size = 1) +
geom_point(data = panama_heather,
aes(x = lon,
y = lat,
color = event),
size = 4) +
theme_map() +
transition_reveal(time)

anim_save("Q5panamaheather.gif")
knitr::include_graphics("Q5panamaheather.gif")
## COVID-19 data
- In this exercise you will animate a map of the US, showing how cumulative COVID-19 cases per 10,000 residents has changed over time. This is similar to exercises 11 & 12 from the previous exercises, with the added animation! So, in the end, you should have something like the static map you made there, but animated over all the days. The code below gives the population estimates for each state and loads the
states_map data. Here is a list of details you should include in the plot:
- Put date in the subtitle.
- Because there are so many dates, you are going to only do the animation for the the 15th of each month. So, filter only to those dates - there are some lubridate functions that can help you do this.
- Use the
animate() function to make the animation 200 frames instead of the default 100 and to pause for 10 frames on the end frame.
- Use
group = date in aes().
- Comment on what you see.
I see
census_pop_est_2018 <- read_csv("https://www.dropbox.com/s/6txwv3b4ng7pepe/us_census_2018_state_pop_est.csv?dl=1") %>%
separate(state, into = c("dot","state"), extra = "merge") %>%
select(-dot) %>%
mutate(state = str_to_lower(state))
states_map <- map_data("state")
covid19_states <- covid19 %>%
arrange(desc(date)) %>%
group_by(state) %>%
#summarise(cases = last(cases)) %>% #this works, but doesn't keep the date (needed later)
mutate(numberrow = 1:n(), #find out what this means in depth
state = str_to_lower(`state`))
covid_19_state_populations <- covid19_states %>%
left_join(census_pop_est_2018,
by = c("state" = "state")) %>%
mutate(covid_per_10000 = (cases/est_pop_2018)*10000)
covid_day_15 <- covid_19_state_populations %>%
filter(day(date) == 15)
us_pandemic <- covid_day_15 %>%
ggplot() +
geom_map(map = states_map,
aes(map_id = state,
fill = covid_per_10000,
group = date)) + #find out why group = date
expand_limits(x = states_map$long, y = states_map$lat) +
labs(title = "COVID-19 Cases in the United States",
subtitle = "Date: {closest_state}",
fill = "Cases per 10,000 people") +
theme_map() +
theme(legend.background = element_blank()) +
transition_states(date, transition_length = 0)
#state_length = 10)
animate(us_pandemic, nframes = 200, end_pause = 10)

anim_save("Q6pandemictide.gif")
knitr::include_graphics("Q6pandemictide.gif")
## Your first shiny app (for next week!)
- This app will also use the COVID data. Make sure you load that data and all the libraries you need in the
app.R file you create. You should create a new project for the app, separate from the homework project. Below, you will post a link to the app that you publish on shinyapps.io. You will create an app to compare states’ daily number of COVID cases per 100,000 over time. The x-axis will be date. You will have an input box where the user can choose which states to compare (selectInput()), a slider where the user can choose the date range, and a submit button to click once the user has chosen all states they’re interested in comparing. The graph should display a different line for each state, with labels either on the graph or in a legend. Color can be used if needed.
Put the link to your app here:
I couldn’t get the shiny to work :(
Here was my code: library(tidyverse)
library(lubridate)
library(shiny)
library(babynames) covid19 <- read_csv(“https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-states.csv”)
ui <- fluidPage(“United States’ daily number of COVID-19 cases per 100,000 people over time.”, sliderInput(inputId = “date”, label = “Date Range”, min = as.Date(min(covid19\(date)), max = as.Date(max(covid19\)date)), value = c(as.Date(min(covid19\(date)), as.Date(max(covid19\)date)))), #textInput(“name”, # “Name”, # value = ““, # placeholder =”Lisa”), selectInput(inputId = “state”, label = “State”, choices = unique(covid19$state)), submitButton(text = “Compare Selected States”), plotOutput(outputId = “covidplot”) )
server <- function(input, output) {
output$covidplot <- renderPlot({ covid19 <- read_csv(“https://raw.githubusercontent.com/nytimes/covid-19-data/master/us-states.csv”)
covid19 <- covid19 %>%
group_by(state) %>%
summarise(cases = last(cases)) %>%
mutate(state = str_to_lower(`state`))
census_pop_est_2018 <- read_csv("https://www.dropbox.com/s/6txwv3b4ng7pepe/us_census_2018_state_pop_est.csv?dl=1") %>%
separate(state, into = c("dot","state"), extra = "merge") %>%
select(-dot) %>%
mutate(state = str_to_lower(state))
covid_19_state_populations <- covid19_states %>%
left_join(census_pop_est_2018,
by = c("state" = "state"))
modified_covid19 <- covid_19_state_populations %>%
mutate(covid_prop = cases/est_pop_2018)
modified_covid19 %>%
#old code
#filter(state %in% state) %>%
#new code - you forgot input$
filter(state %in% input$state) %>%
ggplot() +
#old code
#geom_line(aes(x = date, y = case), color = state) +
#new code - you had case instead of cases and color was outside of aes()
geom_line(aes(x = date, y = cases, color = state)) +
scale_x_continuous(limits = input$date) +
theme_minimal()
}) }
shinyApp(ui = ui, server = server)
LS0tDQp0aXRsZTogJ1dlZWtseSBFeGVyY2lzZXMgIzUnDQphdXRob3I6ICJGZWxpY2lhIFBldGVyc29uIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBrZWVwX21kOiBUUlVFDQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KLS0tDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGVycm9yPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpDQpgYGANCg0KYGBge3IgbGlicmFyaWVzfQ0KbGlicmFyeSh0aWR5dmVyc2UpICAgICAjIGZvciBkYXRhIGNsZWFuaW5nIGFuZCBwbG90dGluZw0KbGlicmFyeShnYXJkZW5SKSAgICAgICAjIGZvciBMaXNhJ3MgZ2FyZGVuIGRhdGENCmxpYnJhcnkobHVicmlkYXRlKSAgICAgIyBmb3IgZGF0ZSBtYW5pcHVsYXRpb24NCmxpYnJhcnkob3BlbmludHJvKSAgICAgIyBmb3IgdGhlIGFiYnIyc3RhdGUoKSBmdW5jdGlvbg0KbGlicmFyeShwYWxtZXJwZW5ndWlucykjIGZvciBQYWxtZXIgcGVuZ3VpbiBkYXRhDQpsaWJyYXJ5KG1hcHMpICAgICAgICAgICMgZm9yIG1hcCBkYXRhDQpsaWJyYXJ5KGdnbWFwKSAgICAgICAgICMgZm9yIG1hcHBpbmcgcG9pbnRzIG9uIG1hcHMNCmxpYnJhcnkoZ3Bsb3RzKSAgICAgICAgIyBmb3IgY29sMmhleCgpIGZ1bmN0aW9uDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikgICMgZm9yIGNvbG9yIHBhbGV0dGVzDQpsaWJyYXJ5KHNmKSAgICAgICAgICAgICMgZm9yIHdvcmtpbmcgd2l0aCBzcGF0aWFsIGRhdGENCmxpYnJhcnkobGVhZmxldCkgICAgICAgIyBmb3IgaGlnaGx5IGN1c3RvbWl6YWJsZSBtYXBwaW5nDQpsaWJyYXJ5KGdndGhlbWVzKSAgICAgICMgZm9yIG1vcmUgdGhlbWVzIChpbmNsdWRpbmcgdGhlbWVfbWFwKCkpDQpsaWJyYXJ5KHBsb3RseSkgICAgICAgICMgZm9yIHRoZSBnZ3Bsb3RseSgpIC0gYmFzaWMgaW50ZXJhY3Rpdml0eQ0KbGlicmFyeShnZ2FuaW1hdGUpICAgICAjIGZvciBhZGRpbmcgYW5pbWF0aW9uIGxheWVycyB0byBnZ3Bsb3RzDQpsaWJyYXJ5KHRyYW5zZm9ybXIpICAgICMgZm9yICJ0d2VlbmluZyIgKGdnYW5pbWF0ZSkNCmxpYnJhcnkoZ2lmc2tpKSAgICAgICAgIyBuZWVkIHRoZSBsaWJyYXJ5IGZvciBjcmVhdGluZyBnaWZzIGJ1dCBkb24ndCBuZWVkIHRvIGxvYWQgZWFjaCB0aW1lDQpsaWJyYXJ5KHNoaW55KSAgICAgICAgICMgZm9yIGNyZWF0aW5nIGludGVyYWN0aXZlIGFwcHMNCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpDQpgYGANCg0KYGBge3IgZGF0YX0NCiMgU05DRiBUcmFpbiBkYXRhDQpzbWFsbF90cmFpbnMgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvbWFzdGVyL2RhdGEvMjAxOS8yMDE5LTAyLTI2L3NtYWxsX3RyYWlucy5jc3YiKSANCg0KIyBMaXNhJ3MgZ2FyZGVuIGRhdGENCmRhdGEoImdhcmRlbl9oYXJ2ZXN0IikNCg0KIyBMaXNhJ3MgTWFsbG9yY2EgY3ljbGluZyBkYXRhDQptYWxsb3JjYV9iaWtlX2RheTcgPC0gcmVhZF9jc3YoImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3MvemM2amFuNGx0bWp0dnkwL21hbGxvcmNhX2Jpa2VfZGF5Ny5jc3Y/ZGw9MSIpICU+JSANCiAgc2VsZWN0KDE6NCwgc3BlZWQpDQoNCiMgSGVhdGhlciBMZW5kd2F5J3MgSXJvbm1hbiA3MC4zIFBhbiBBbSBjaGFtcGlvbnNoaXBzIFBhbmFtYSBkYXRhDQpwYW5hbWFfc3dpbSA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2xsZW5kd2F5L2dwcy1kYXRhL21hc3Rlci9kYXRhL3BhbmFtYV9zd2ltXzIwMTYwMTMxLmNzdiIpDQoNCnBhbmFtYV9iaWtlIDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbGxlbmR3YXkvZ3BzLWRhdGEvbWFzdGVyL2RhdGEvcGFuYW1hX2Jpa2VfMjAxNjAxMzEuY3N2IikNCg0KcGFuYW1hX3J1biA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2xsZW5kd2F5L2dwcy1kYXRhL21hc3Rlci9kYXRhL3BhbmFtYV9ydW5fMjAxNjAxMzEuY3N2IikNCg0KI0NPVklELTE5IGRhdGEgZnJvbSB0aGUgTmV3IFlvcmsgVGltZXMNCmNvdmlkMTkgPC0gcmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ueXRpbWVzL2NvdmlkLTE5LWRhdGEvbWFzdGVyL3VzLXN0YXRlcy5jc3YiKQ0KDQpgYGANCg0KIyMgUHV0IHlvdXIgaG9tZXdvcmsgb24gR2l0SHViIQ0KDQpHbyBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2xsZW5kd2F5L2dpdGh1Yl9mb3JfY29sbGFib3JhdGlvbi9ibG9iL21hc3Rlci9naXRodWJfZm9yX2NvbGxhYm9yYXRpb24ubWQpIG9yIHRvIHByZXZpb3VzIGhvbWV3b3JrIHRvIHJlbWluZCB5b3Vyc2VsZiBob3cgdG8gZ2V0IHNldCB1cC4gDQoNCk9uY2UgeW91ciByZXBvc2l0b3J5IGlzIGNyZWF0ZWQsIHlvdSBzaG91bGQgYWx3YXlzIG9wZW4geW91ciAqKnByb2plY3QqKiByYXRoZXIgdGhhbiBqdXN0IG9wZW5pbmcgYW4gLlJtZCBmaWxlLiBZb3UgY2FuIGRvIHRoYXQgYnkgZWl0aGVyIGNsaWNraW5nIG9uIHRoZSAuUnByb2ogZmlsZSBpbiB5b3VyIHJlcG9zaXRvcnkgZm9sZGVyIG9uIHlvdXIgY29tcHV0ZXIuIE9yLCBieSBnb2luZyB0byB0aGUgdXBwZXIgcmlnaHQgaGFuZCBjb3JuZXIgaW4gUiBTdHVkaW8gYW5kIGNsaWNraW5nIHRoZSBhcnJvdyBuZXh0IHRvIHdoZXJlIGl0IHNheXMgUHJvamVjdDogKE5vbmUpLiBZb3Ugc2hvdWxkIHNlZSB5b3VyIHByb2plY3QgY29tZSB1cCBpbiB0aGF0IGxpc3QgaWYgeW91J3ZlIHVzZWQgaXQgcmVjZW50bHkuIFlvdSBjb3VsZCBhbHNvIGdvIHRvIEZpbGUgLS0+IE9wZW4gUHJvamVjdCBhbmQgbmF2aWdhdGUgdG8geW91ciAuUnByb2ogZmlsZS4gDQoNCiMjIEluc3RydWN0aW9ucw0KDQoqIFB1dCB5b3VyIG5hbWUgYXQgdGhlIHRvcCBvZiB0aGUgZG9jdW1lbnQuIA0KDQoqICoqRm9yIEFMTCBncmFwaHMsIHlvdSBzaG91bGQgaW5jbHVkZSBhcHByb3ByaWF0ZSBsYWJlbHMgYW5kIGFsdCB0ZXh0LioqIA0KDQoqIEZlZWwgZnJlZSB0byBjaGFuZ2UgdGhlIGRlZmF1bHQgdGhlbWUsIHdoaWNoIEkgY3VycmVudGx5IGhhdmUgc2V0IHRvIGB0aGVtZV9taW5pbWFsKClgLiANCg0KKiBVc2UgZ29vZCBjb2RpbmcgcHJhY3RpY2UuIFJlYWQgdGhlIHNob3J0IHNlY3Rpb25zIG9uIGdvb2QgY29kZSB3aXRoIFtwaXBlc10oaHR0cHM6Ly9zdHlsZS50aWR5dmVyc2Uub3JnL3BpcGVzLmh0bWwpIGFuZCBbZ2dwbG90Ml0oaHR0cHM6Ly9zdHlsZS50aWR5dmVyc2Uub3JnL2dncGxvdDIuaHRtbCkuICoqVGhpcyBpcyBwYXJ0IG9mIHlvdXIgZ3JhZGUhKioNCg0KKiAqKk5FVyEhKiogV2l0aCBhbmltYXRlZCBncmFwaHMsIGFkZCBgZXZhbD1GQUxTRWAgdG8gdGhlIGNvZGUgY2h1bmsgdGhhdCBjcmVhdGVzIHRoZSBhbmltYXRpb24gYW5kIHNhdmVzIGl0IHVzaW5nIGBhbmltX3NhdmUoKWAuIEFkZCBhbm90aGVyIGNvZGUgY2h1bmsgdG8gcmVyZWFkIHRoZSBnaWYgYmFjayBpbnRvIHRoZSBmaWxlLiBTZWUgdGhlIFt0dXRvcmlhbF0oaHR0cHM6Ly9hbmltYXRpb24tYW5kLWludGVyYWN0aXZpdHktaW4tci5uZXRsaWZ5LmFwcC8pIGZvciBoZWxwLiANCg0KKiBXaGVuIHlvdSBhcmUgZmluaXNoZWQgd2l0aCBBTEwgdGhlIGV4ZXJjaXNlcywgdW5jb21tZW50IHRoZSBvcHRpb25zIGF0IHRoZSB0b3Agc28geW91ciBkb2N1bWVudCBsb29rcyBuaWNlci4gRG9uJ3QgZG8gaXQgYmVmb3JlIHRoZW4sIG9yIGVsc2UgeW91IG1pZ2h0IG1pc3Mgc29tZSBpbXBvcnRhbnQgd2FybmluZ3MgYW5kIG1lc3NhZ2VzLg0KDQojIyBXYXJtLXVwIGV4ZXJjaXNlcyBmcm9tIHR1dG9yaWFsDQoNCiAgMS4gQ2hvb3NlIDIgZ3JhcGhzIHlvdSBoYXZlIGNyZWF0ZWQgZm9yIEFOWSBhc3NpZ25tZW50IGluIHRoaXMgY2xhc3MgYW5kIGFkZCBpbnRlcmFjdGl2aXR5IHVzaW5nIHRoZSBgZ2dwbG90bHkoKWAgZnVuY3Rpb24uDQogIA0KYGBge3J9DQoNCnJhdGluZ3MgPC0gcmVhZHI6OnJlYWRfY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMjIvMjAyMi0wMS0yNS9yYXRpbmdzLmNzdicpDQpkZXRhaWxzIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIyLzIwMjItMDEtMjUvZGV0YWlscy5jc3YnKQ0KDQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKQ0KDQpgYGANCiAgDQogIA0KYGBge3IsIGZpZy5hbHQ9ICJSZW1lbWJlciB0byBkZXNjcmliZSBoZXJlIn0NCg0KYm9hcmRnYW1lX2hhdmVuIDwtIHJhdGluZ3MgJT4lIA0KICBsZWZ0X2pvaW4oZGV0YWlscywNCiAgICAgICAgICAgIGJ5ID0gYygiaWQiID0gImlkIikpDQoNCnNpbmdsZV9wYXJlbnRfb25seV9jaGlsZF9ob3VzZWhvbGRfZ2FtZXNfbG9sIDwtIGJvYXJkZ2FtZV9oYXZlbiAlPiUgDQogIGZpbHRlcihtYXhwbGF5ZXJzID09IDIpICU+JSANCiAgYXJyYW5nZShyYW5rKSAlPiUgDQogIHNsaWNlX2hlYWQobiA9IDEwKSAlPiUgDQogIGdncGxvdCgpICsNCiAgZ2VvbV9jb2woYWVzKHggPSB1c2Vyc19yYXRlZCwNCiAgICAgICAgICAgICAgIHkgPSBmY3RfcmVvcmRlcihuYW1lLCB1c2Vyc19yYXRlZCwgLmRlc2MgPSBGQUxTRSksDQogICAgICAgICAgICAgICBmaWxsID0gbmFtZSwNCiAgICAgICAgICAgICAgIHRleHQgPSBuYW1lKSkgKyANCiAgbGFicyh0aXRsZSA9ICJUb3AgMTAgR2FtZXMgZm9yIFR3by1QbGF5ZXJzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJUaGlzIGdyYXBoIHNob3dzIHRoZSBudW1iZXIgb2YgdXNlcnMgd2hvIHJhdGVkIGVhY2ggZ2FtZS4iLA0KICAgICAgIHggPSAiIiwNCiAgICAgICB5ID0gIiIpICsgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSkNCiAgDQoNCmBgYA0KDQpgYGB7cn0NCmRhdGEocGVuZ3VpbnMpDQpwZW5ndWlucyA8LSBwZW5ndWlucyAlPiUgDQogIG11dGF0ZShBbmdyeUJpcmQgPSBtZWFuKGJpbGxfbGVuZ3RoX21tLCBuYS5ybSA9IFRSVUUpKQ0KDQp3YWRkbGVzIDwtIHBlbmd1aW5zICU+JSANCiAgZ2dwbG90KGFlcyh4PWJpbGxfbGVuZ3RoX21tLCB0ZXh0ID0gQW5ncnlCaXJkKSkrDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPSAyLCBmaWxsPSJwdXJwbGU0IikgKyANCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PW1lYW4ocGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0sIG5hLnJtID0gVFJVRSksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsgI25hLnJtPVRSVUUgdG8gcmVtb3ZlIHRoZSBuYSBsaW5lcw0KICBsYWJzKHRpdGxlPSJUaGVzZSBCaWxscyBEbyBOb3QgUGF5IEJ1dCBUaGV5J3JlIEdyZWF0IEFueXdheSIsDQogICAgICAgICAgICAgeCA9ICJCaWxsIExlbmd0aCAobW0pIiwNCiAgICAgICB5ID0gIk51bWJlciBvZiBQZW5ndWlucyIpDQpgYGAgIA0KDQoNCmBgYHtyLCBmaWcuYWx0ID0gIkFuIGludGVyYWN0aXZlIGdyYXBoIG9mIHRoZSB0b3AgMTAgZ2FtZXMgZm9yIHR3byBwbGF5ZXJzLiIgfQ0KZ2dwbG90bHkoc2luZ2xlX3BhcmVudF9vbmx5X2NoaWxkX2hvdXNlaG9sZF9nYW1lc19sb2wsDQogICAgICAgICB0b29sdGlwID0gYygidGV4dCIsICJ4IikpDQoNCmBgYA0KIA0KYGBge3IsIGZpZy5hbHQgPSAiQW4gaW50ZXJhY3RpdmUgZ3JhcGggc2hvd2luZyBwZW5ndWluIGJpbGwgbGVuZ3RocyBhbmQgYSByZWQgZG90dGVkIGxpbmUgc2hvd2luZyB0aGUgbWVkaWFuIHBlbmd1aW4gYmlsbCBsZW5ndGggYXJvdW5kIDQ1IG1tLiJ9DQpnZ3Bsb3RseSh3YWRkbGVzLA0KICAgICAgICAgdG9vbHRpcCA9IGMoInRleHQiLCAieCIpKQ0KYGBgDQogDQogIDIuIFVzZSBhbmltYXRpb24gdG8gdGVsbCBhbiBpbnRlcmVzdGluZyBzdG9yeSB3aXRoIHRoZSBgc21hbGxfdHJhaW5zYCBkYXRhc2V0IHRoYXQgY29udGFpbnMgZGF0YSBmcm9tIHRoZSBTTkNGIChOYXRpb25hbCBTb2NpZXR5IG9mIEZyZW5jaCBSYWlsd2F5cykuIFRoZXNlIGFyZSBUaWR5IFR1ZXNkYXkgZGF0YSEgUmVhZCBtb3JlIGFib3V0IGl0IFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L3RyZWUvbWFzdGVyL2RhdGEvMjAxOS8yMDE5LTAyLTI2KS4NCg0KYGBge3J9DQoNCnNtYWxsX3RyYWlucyAlPiUgDQogIGZpbHRlcihzZXJ2aWNlID09ICJJbnRlcm5hdGlvbmFsIikgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBkZWxheWVkX251bWJlciwgDQogICAgICAgICAgICAgeSA9IGRlbGF5X2NhdXNlLA0KICAgICAgICAgICAgIGdyb3VwID0geWVhcikpICsNCiAgZ2VvbV9qaXR0ZXIoKSArIA0KICBsYWJzKHRpdGxlID0gIkludGVybmF0aW9uYWwgVHJhaW4gU2VydmljZSBpbiBGcmFuY2UgYW5kIENhdXNlcyBmb3IgRGVsYXkiLA0KICAgICAgIHN1YnRpdGxlID0gIlllYXI6IHtmcmFtZV90aW1lfSIsDQogICAgICAgeSA9ICIiLA0KICAgICAgIHggPSAiUGVyY2VudCBvZiB0cmFpbnMgZGVsYXllZCIpICsNCiAgdHJhbnNpdGlvbl90aW1lKHllYXIpDQoNCmFuaW1fc2F2ZSgiUTJmcmVuY2h0cmFpbnMuZ2lmIikNCg0KDQpgYGANCg0KYGBge3IsIGZpZy5hbHQ9ICJUaGlzIGdpZiBzaG93cyB0aGUgcGVyY2VudCBvZiBkaWZmZXJlbnQgdHlwZXMgb2YgZGVsYXlzIGludGVybmF0aW9uYWwgdHJhaW4gc2VydmljZSBpbiBGcmFuY2UgZmFjZSBvdmVyIHRoZSB5ZWFycy4gT3ZlcmFsbCwgdHJhZmZpYyBtYW5hZ2VtZW50IHNlZW1zIHRvIGJlIGEgY29uc2lzdGVudGx5IGhpZ2ggY2F1c2Ugb2YgdHJpYW4gZGVsYXlzLiIgfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIlEyZnJlbmNodHJhaW5zLmdpZiIpDQpgYGANCg0KIyMgR2FyZGVuIGRhdGENCg0KICAzLiBJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBjcmVhdGUgYSBzdGFja2VkIGFyZWEgcGxvdCB0aGF0IHJldmVhbHMgaXRzZWxmIG92ZXIgdGltZSAoc2VlIHRoZSBgZ2VvbV9hcmVhKClgIGV4YW1wbGVzIFtoZXJlXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvcG9zaXRpb25fc3RhY2suaHRtbCkpLiBZb3Ugd2lsbCBsb29rIGF0IGN1bXVsYXRpdmUgaGFydmVzdCBvZiB0b21hdG8gdmFyaWV0aWVzIG92ZXIgdGltZS4gWW91IHNob3VsZCBkbyB0aGUgZm9sbG93aW5nOg0KICAqIEZyb20gdGhlIGBnYXJkZW5faGFydmVzdGAgZGF0YSwgZmlsdGVyIHRoZSBkYXRhIHRvIHRoZSB0b21hdG9lcyBhbmQgZmluZCB0aGUgKmRhaWx5KiBoYXJ2ZXN0IGluIHBvdW5kcyBmb3IgZWFjaCB2YXJpZXR5LiAgDQogICogVGhlbiwgZm9yIGVhY2ggdmFyaWV0eSwgZmluZCB0aGUgY3VtdWxhdGl2ZSBoYXJ2ZXN0IGluIHBvdW5kcy4gIA0KICAqIFVzZSB0aGUgZGF0YSB5b3UganVzdCBtYWRlIHRvIGNyZWF0ZSBhIHN0YXRpYyBjdW11bGF0aXZlIGhhcnZlc3QgYXJlYSBwbG90LCB3aXRoIHRoZSBhcmVhcyBmaWxsZWQgd2l0aCBkaWZmZXJlbnQgY29sb3JzIGZvciBlYWNoIHZhcmlldHkgYW5kIGFycmFuZ2VkIChISU5UOiBgZmN0X3Jlb3JkZXIoKWApIGZyb20gbW9zdCB0byBsZWFzdCBoYXJ2ZXN0ZWQgd2VpZ2h0cyAobW9zdCBvbiB0aGUgYm90dG9tKS4gIA0KICAqIEFkZCBhbmltYXRpb24gdG8gcmV2ZWFsIHRoZSBwbG90IG92ZXIgZGF0ZS4gDQoNCkkgaGF2ZSBzdGFydGVkIHRoZSBjb2RlIGZvciB5b3UgYmVsb3cuIFRoZSBgY29tcGxldGUoKWAgZnVuY3Rpb24gY3JlYXRlcyBhIHJvdyBmb3IgYWxsIHVuaXF1ZSBgZGF0ZWAvYHZhcmlldHlgIGNvbWJpbmF0aW9ucy4gSWYgYSB2YXJpZXR5IGlzIG5vdCBoYXJ2ZXN0ZWQgb24gb25lIG9mIHRoZSBoYXJ2ZXN0IGRhdGVzIGluIHRoZSBkYXRhc2V0LCBpdCBpcyBmaWxsZWQgd2l0aCBhIHZhbHVlIG9mIDAuDQoNCmBgYHtyfQ0KZGFpbHlfdG9tYXRvIDwtIGdhcmRlbl9oYXJ2ZXN0ICU+JSANCiAgZmlsdGVyKHZlZ2V0YWJsZSA9PSAidG9tYXRvZXMiKSAlPiUgDQogIGdyb3VwX2J5KGRhdGUsIHZhcmlldHkpICU+JSANCiAgc3VtbWFyaXplKGRhaWx5X2hhcnZlc3RfbGIgPSBzdW0od2VpZ2h0KSowLjAwMjIwNDYyKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGNvbXBsZXRlKHZhcmlldHksIA0KICAgICAgICAgICBkYXRlLCANCiAgICAgICAgICAgZmlsbCA9IGxpc3QoZGFpbHlfaGFydmVzdF9sYiA9IDApKSAlPiUgDQogIG11dGF0ZSh2YXJpZXR5ID0gZmN0X3Jlb3JkZXIodmFyaWV0eSwgZGFpbHlfaGFydmVzdF9sYiwgc3VtKSkgJT4lDQogIGdyb3VwX2J5KHZhcmlldHkpICU+JSANCiAgbXV0YXRlKGN1bXN1bV90b21hdG8gPSBjdW1zdW0oZGFpbHlfaGFydmVzdF9sYikpICAjMS4gd2hpY2ggdmFyaWFibGUgZG8geW91IHdhbnQgdG8gcmVvcmRlciwgMi4gYnkgd2hpY2ggdmFyaWFibGUsIGZ1bmN0aW9uIHlvdSB3YW50IHRvIHVzZT8NCg0KICANCg0KYGBgDQoNCmBgYHtyfQ0KZGFpbHlfdG9tYXRvICU+JSANCmdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSBjdW1zdW1fdG9tYXRvLCBmaWxsID0gdmFyaWV0eSkpICsgDQogIGdlb21fYXJlYSgpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHZhcmlldHkpLCBwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgbGFicyh0aXRsZSA9ICJUb21hdG8gVGltZTogQ3VtdWxhdGl2ZSBIYXJ2ZXN0IGluIFBvdW5kcyBPdmVyIFRpbWUiLA0KICAgICAgIHN1YnRpdGxlID0gIkRhdGU6IHtmcmFtZV9hbG9uZ30iLA0KICAgICAgIHggPSAiIiwNCiAgICAgICB5ID0gIiIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIHRyYW5zaXRpb25fcmV2ZWFsKGRhdGUpIA0KDQphbmltX3NhdmUoIlEzdG9tYXRvaGFydmVzdC5naWYiKQ0KICANCg0KI3RyYW5zaXRpb24gZGF0ZSBvbmx5IHNob3dzIHdoYXQgaGFwcGVucyBvbiBhIHNwZWNpZmljIGRhdGU7IHRyYW5zaXRpb24gcmV2ZWFsIHNob3dzIHByb2dyZXNzaW9uDQpgYGANCmBgYHtyLCBhbHQuZmlnID0gIlRoaXMgaXMgYSBnaWZ0IHNob3dpbmcgdGhlIGN1bXVsYXRpdmUgaGFydmVzdCBvdmVyIHRpbWUgb2YgUHJvZmVzc29yIExlbmR3YXkncyBkaWZmZXJlbnQgdG9tYXRvIHZhcmlldGllcy4gSmV0IFN0YXIgaXMgb24gdGhlIHRvcC4ifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIlEzdG9tYXRvaGFydmVzdC5naWYiKQ0KYGBgDQojIyBNYXBzLCBhbmltYXRpb24sIGFuZCBtb3ZlbWVudCENCg0KICA0LiBNYXAgTGlzYSdzIGBtYWxsb3JjYV9iaWtlX2RheTdgIGJpa2UgcmlkZSB1c2luZyBhbmltYXRpb24hIA0KICBSZXF1aXJlbWVudHM6DQogICogUGxvdCBvbiBhIG1hcCB1c2luZyBgZ2dtYXBgLiAgDQogICogU2hvdyAiY3VycmVudCIgbG9jYXRpb24gd2l0aCBhIHJlZCBwb2ludC4gDQogICogU2hvdyBwYXRoIHVwIHVudGlsIHRoZSBjdXJyZW50IHBvaW50LiAgDQogICogQ29sb3IgdGhlIHBhdGggYWNjb3JkaW5nIHRvIGVsZXZhdGlvbi4gIA0KICAqIFNob3cgdGhlIHRpbWUgaW4gdGhlIHN1YnRpdGxlLiAgDQogICogQ0hBTExFTkdFOiB1c2UgdGhlIGBnZ2ltYWdlYCBwYWNrYWdlIGFuZCBgZ2VvbV9pbWFnZWAgdG8gYWRkIGEgYmlrZSBpbWFnZSBpbnN0ZWFkIG9mIGEgcmVkIHBvaW50LiBZb3UgY2FuIHVzZSBbdGhpc10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2xsZW5kd2F5L2FuaW1hdGlvbl9hbmRfaW50ZXJhY3Rpdml0eS9tYXN0ZXIvYmlrZS5wbmcpIGltYWdlLiBTZWUgW2hlcmVdKGh0dHBzOi8vZ29vZGVrYXQuZ2l0aHViLmlvL3ByZXNlbnRhdGlvbnMvMjAxOS1pc3VnZy1nZ2FuaW1hdGUtc3Bvb2t5L3NsaWRlcy5odG1sIzM1KSBmb3IgYW4gZXhhbXBsZS4gDQogICogQWRkIHNvbWV0aGluZyBvZiB5b3VyIG93biEgQW5kIGNvbW1lbnQgb24gaWYgeW91IHByZWZlciB0aGlzIHRvIHRoZSBzdGF0aWMgbWFwIGFuZCB3aHkgb3Igd2h5IG5vdC4NCiAgDQpgYGB7cn0NCm1hbGxvcmNhX21hcCA8LSBnZXRfc3RhbWVubWFwKA0KICBiYm94ID0gYyggbGVmdCA9IDIuMTYyOSwgYm90dG9tID0gMzkuMjI0OCwgcmlnaHQgPSAzLjY1OTgsIHRvcCA9IDQwLjAwMTMpLA0KICBtYXB0eXBlID0gInRlcnJhaW4iLA0KICB6b29tID0gMTEpDQoNCmdnbWFwKG1hbGxvcmNhX21hcCkgKw0KICBnZW9tX3BhdGgoZGF0YSA9IG1hbGxvcmNhX2Jpa2VfZGF5NywNCiAgICAgICAgICAgIGFlcyh4ID0gbG9uLA0KICAgICAgICAgICAgICAgIHkgPSBsYXQsIA0KICAgICAgICAgICAgICAgIGNvbG9yID0gZWxlKSwNCiAgICAgICAgICAgIHNpemUgPSAxKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IG1hbGxvcmNhX2Jpa2VfZGF5NywNCiAgICAgICAgICAgICBhZXMoeCA9IGxvbiwgDQogICAgICAgICAgICAgICAgIHkgPSBsYXQpLA0KICAgICAgICAgICAgIHNpemUgPSAyLA0KICAgICAgICAgICAgIGNvbG9yID0gInJlZCIpICsNCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJtYWdtYSIpICsNCiAgdGhlbWVfbWFwKCkgKw0KICBsYWJzKHRpdGxlID0gIkxpc2EncyBNYWxsb3JjYSBCaWtlIFJpZGU6IE1vcmUgRWxldmF0ZWQgVGhhbiBFdmVyIEJlZm9yZSIsIA0KICAgICAgIHN1YnRpdGxlID0gICJEYXRlOiB7ZnJhbWVfYWxvbmd9IiwNCiAgICAgICB4ID0gIiIsDQogICAgICAgeSA9ICIiKSArDQogIHRoZW1lKGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArDQogIHRyYW5zaXRpb25fcmV2ZWFsKHRpbWUpDQoNCmFuaW1fc2F2ZSgiUTRtYWxsb3JjYWJpa2UuZ2lmIikNCg0KYGBgDQogDQpgYGB7ciwgIGZpZy5hbHQ9ICJUaGlzIGlzIGEgZ2lmdCBvZiBQcm9mZXNzb3IgTGVuZHdheSdzIGJpa2luZyB0cmlwIG9uIE1hbGxvcmNhLiBUaGVyZSBpcyBhIGRvdCBzaG93aW5nIHdoZXJlIHNoZSBpcyBhdCBhbnkgZ2l2ZW4gcG9pbnQgb2YgdGltZSBhbmQgdGhlIGRpZmZlcmVudCBzaGFkZXMgb2YgY29sb3Igb24gdGhlIHBhdGggc2lnbmlmeSB0aGUgZGlmZmVyZW50IGVsZXZhdGlvbnMgc2hlIHJvZGUgb24uIFRoZSBkYXJrZXN0IGNvbG9ycyBhcmUgdGhlIGxvd2VzdCBsZXZlbHMsIHdoaWxlIHRoZSBsaWdodGVzdCBjb2xvcnMgYXJlIGhpZ2hlciBpbiBlbGV2YXRpb24uIn0NCg0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIlE0bWFsbG9yY2FiaWtlLmdpZiIpDQpgYGANCiAgNS4gSW4gdGhpcyBleGVyY2lzZSwgeW91IGdldCB0byBtZWV0IExpc2EncyBzaXN0ZXIsIEhlYXRoZXIhIFNoZSBpcyBhIHByb3VkIE1hYyBncmFkLCBjdXJyZW50bHkgd29ya3MgYXMgYSBEYXRhIFNjaWVudGlzdCB3aGVyZSBzaGUgdXNlcyBSIGV2ZXJ5ZGF5LCBhbmQgZm9yIGEgZmV3IHllYXJzICh3aGlsZSBzdGlsbCBob2xkaW5nIGEgZnVsbC10aW1lIGpvYikgc2hlIHdhcyBhIHBybyB0cmlhdGhsZXRlLiBZb3UgYXJlIGdvaW5nIHRvIG1hcCBvbmUgb2YgaGVyIHJhY2VzLiBUaGUgZGF0YSBmcm9tIGVhY2ggZGlzY2lwbGluZSBvZiB0aGUgSXJvbm1hbiA3MC4zIFBhbiBBbSBjaGFtcGlvbnNoaXBzLCBQYW5hbWEgaXMgaW4gYSBzZXBhcmF0ZSBmaWxlIC0gYHBhbmFtYV9zd2ltYCwgYHBhbmFtYV9iaWtlYCwgYW5kIGBwYW5hbWFfcnVuYC4gQ3JlYXRlIGEgc2ltaWxhciBtYXAgdG8gdGhlIG9uZSB5b3UgY3JlYXRlZCB3aXRoIG15IGN5Y2xpbmcgZGF0YS4gWW91IHdpbGwgbmVlZCB0byBtYWtlIHNvbWUgc21hbGwgY2hhbmdlczogMS4gY29tYmluZSB0aGUgZmlsZXMgcHV0dGluZyB0aGVtIGluIHN3aW0sIGJpa2UsIHJ1biBvcmRlciAoSElOVDogYGJpbmRfcm93cygpYCksIDIuIG1ha2UgdGhlIGxlYWRpbmcgZG90IGEgZGlmZmVyZW50IGNvbG9yIGRlcGVuZGluZyBvbiB0aGUgZXZlbnQgKGZvciBhbiBleHRyYSBjaGFsbGVuZ2UsIG1ha2UgaXQgYSBkaWZmZXJlbnQgaW1hZ2UgdXNpbmcgYGdlb21faW1hZ2UoKSEpLCAzLiBDSEFMTEVOR0UgKG9wdGlvbmFsKTogY29sb3IgYnkgc3BlZWQsIHdoaWNoIHlvdSB3aWxsIG5lZWQgdG8gY29tcHV0ZSBvbiB5b3VyIG93biBmcm9tIHRoZSBkYXRhLiBZb3UgY2FuIHJlYWQgSGVhdGhlcidzIHJhY2UgcmVwb3J0IFtoZXJlXShodHRwczovL2hlYXRoZXJsZW5kd2F5LmNvbS8yMDE2LzAyLzEwL2lyb25tYW4tNzAtMy1wYW4tYW1lcmljYW4tY2hhbXBpb25zaGlwcy1wYW5hbWEtcmFjZS1yZXBvcnQvKS4gU2hlIGlzIGFsc28gaW4gdGhlIE1hY2FsZXN0ZXIgQXRobGV0aWNzIFtIYWxsIG9mIEZhbWVdKGh0dHBzOi8vYXRobGV0aWNzLm1hY2FsZXN0ZXIuZWR1L2hvbm9ycy9oYWxsLW9mLWZhbWUvaGVhdGhlci1sZW5kd2F5LzE4NCkgYW5kIHN0aWxsIGhhcyByZWNvcmRzIGF0IHRoZSBwb29sLiANCiAgDQpgYGB7cn0NCnBhbmFtYV9tYXAgPC0gZ2V0X3N0YW1lbm1hcCgNCiAgYmJveCA9IGMoIGxlZnQgPSAtNzkuNjg2NiwgYm90dG9tID0gOC44MjU0LCByaWdodCA9IC03OS40ODE3LCB0b3AgPSA4Ljk5NjYpLA0KICBtYXB0eXBlID0gInRlcnJhaW4iLA0KICB6b29tID0gMTEpDQoNCnBhbmFtYV9oZWF0aGVyIDwtIGJpbmRfcm93cyhsaXN0KHBhbmFtYV9zd2ltLCBwYW5hbWFfYmlrZSwgcGFuYW1hX3J1bikpDQpgYGANCiAgDQpgYGB7cn0NCmdnbWFwKHBhbmFtYV9tYXApICsNCiAgZ2VvbV9wYXRoKGRhdGEgPSBwYW5hbWFfaGVhdGhlciwNCiAgICAgICAgICAgIGFlcyh4ID0gbG9uLA0KICAgICAgICAgICAgICAgIHkgPSBsYXQsIA0KICAgICAgICAgICAgICAgIGNvbG9yID0gZXZlbnQpLA0KICAgICAgICAgICAgc2l6ZSA9IDEpICsNCiAgZ2VvbV9wb2ludChkYXRhID0gcGFuYW1hX2hlYXRoZXIsDQogICAgICAgICAgICAgYWVzKHggPSBsb24sIA0KICAgICAgICAgICAgICAgICB5ID0gbGF0LA0KICAgICAgICAgICAgICAgICBjb2xvciA9IGV2ZW50KSwNCiAgICAgICAgICAgICBzaXplID0gNCkgKw0KDQogIHRoZW1lX21hcCgpICsNCiAgdHJhbnNpdGlvbl9yZXZlYWwodGltZSkNCg0KYW5pbV9zYXZlKCJRNXBhbmFtYWhlYXRoZXIuZ2lmIikNCg0KDQoNCmBgYA0KICANCmBgYHtyLCBmaWcuYWx0PSAiVGhpcyBpcyBhIGdpZiBzaG93aW5nIEhlYXRoZXIncyBJcm9ubWFuIDcwLjMgUGFuIEFtIENoYW1waW9uc2hpcHMgaW4gd2hpY2ggc2hlIHN3aW1zLCBiaWtlcywgYW5kIHJ1bnMuIEVhY2ggbGVnIG9mIGhlciByYWNlIGlzIHNob3duIGluIGEgZGlmZmVyZW50IGNvbG9yLiJ9DQoNCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJRNXBhbmFtYWhlYXRoZXIuZ2lmIikNCmBgYA0KIyMgQ09WSUQtMTkgZGF0YQ0KDQogIDYuIEluIHRoaXMgZXhlcmNpc2UgeW91IHdpbGwgYW5pbWF0ZSBhIG1hcCBvZiB0aGUgVVMsIHNob3dpbmcgaG93IGN1bXVsYXRpdmUgQ09WSUQtMTkgY2FzZXMgcGVyIDEwLDAwMCByZXNpZGVudHMgaGFzIGNoYW5nZWQgb3ZlciB0aW1lLiBUaGlzIGlzIHNpbWlsYXIgdG8gZXhlcmNpc2VzIDExICYgMTIgZnJvbSB0aGUgcHJldmlvdXMgZXhlcmNpc2VzLCB3aXRoIHRoZSBhZGRlZCBhbmltYXRpb24hIFNvLCBpbiB0aGUgZW5kLCB5b3Ugc2hvdWxkIGhhdmUgc29tZXRoaW5nIGxpa2UgdGhlIHN0YXRpYyBtYXAgeW91IG1hZGUgdGhlcmUsIGJ1dCBhbmltYXRlZCBvdmVyIGFsbCB0aGUgZGF5cy4gVGhlIGNvZGUgYmVsb3cgZ2l2ZXMgdGhlIHBvcHVsYXRpb24gZXN0aW1hdGVzIGZvciBlYWNoIHN0YXRlIGFuZCBsb2FkcyB0aGUgYHN0YXRlc19tYXBgIGRhdGEuIEhlcmUgaXMgYSBsaXN0IG9mIGRldGFpbHMgeW91IHNob3VsZCBpbmNsdWRlIGluIHRoZSBwbG90Og0KICANCiAgKiBQdXQgZGF0ZSBpbiB0aGUgc3VidGl0bGUuICAgDQogICogQmVjYXVzZSB0aGVyZSBhcmUgc28gbWFueSBkYXRlcywgeW91IGFyZSBnb2luZyB0byBvbmx5IGRvIHRoZSBhbmltYXRpb24gZm9yIHRoZSB0aGUgMTV0aCBvZiBlYWNoIG1vbnRoLiBTbywgZmlsdGVyIG9ubHkgdG8gdGhvc2UgZGF0ZXMgLSB0aGVyZSBhcmUgc29tZSBsdWJyaWRhdGUgZnVuY3Rpb25zIHRoYXQgY2FuIGhlbHAgeW91IGRvIHRoaXMuICAgDQogICogVXNlIHRoZSBgYW5pbWF0ZSgpYCBmdW5jdGlvbiB0byBtYWtlIHRoZSBhbmltYXRpb24gMjAwIGZyYW1lcyBpbnN0ZWFkIG9mIHRoZSBkZWZhdWx0IDEwMCBhbmQgdG8gcGF1c2UgZm9yIDEwIGZyYW1lcyBvbiB0aGUgZW5kIGZyYW1lLiAgIA0KICAqIFVzZSBgZ3JvdXAgPSBkYXRlYCBpbiBgYWVzKClgLiAgDQogICogQ29tbWVudCBvbiB3aGF0IHlvdSBzZWUuICANCiAgDQogIF9fSSBzZWVfXw0KDQpgYGB7cn0NCmNlbnN1c19wb3BfZXN0XzIwMTggPC0gcmVhZF9jc3YoImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3MvNnR4d3YzYjRuZzdwZXBlL3VzX2NlbnN1c18yMDE4X3N0YXRlX3BvcF9lc3QuY3N2P2RsPTEiKSAlPiUgDQogIHNlcGFyYXRlKHN0YXRlLCBpbnRvID0gYygiZG90Iiwic3RhdGUiKSwgZXh0cmEgPSAibWVyZ2UiKSAlPiUgDQogIHNlbGVjdCgtZG90KSAlPiUgDQogIG11dGF0ZShzdGF0ZSA9IHN0cl90b19sb3dlcihzdGF0ZSkpDQoNCnN0YXRlc19tYXAgPC0gbWFwX2RhdGEoInN0YXRlIikNCg0KY292aWQxOV9zdGF0ZXMgPC0gY292aWQxOSAlPiUgDQogIGFycmFuZ2UoZGVzYyhkYXRlKSkgJT4lIA0KICBncm91cF9ieShzdGF0ZSkgJT4lDQogICNzdW1tYXJpc2UoY2FzZXMgPSBsYXN0KGNhc2VzKSkgJT4lICN0aGlzIHdvcmtzLCBidXQgZG9lc24ndCBrZWVwIHRoZSBkYXRlIChuZWVkZWQgbGF0ZXIpDQogIG11dGF0ZShudW1iZXJyb3cgPSAxOm4oKSwgI2ZpbmQgb3V0IHdoYXQgdGhpcyBtZWFucyBpbiBkZXB0aA0KICAgIHN0YXRlID0gc3RyX3RvX2xvd2VyKGBzdGF0ZWApKQ0KDQoNCmNvdmlkXzE5X3N0YXRlX3BvcHVsYXRpb25zIDwtIGNvdmlkMTlfc3RhdGVzICU+JSANCiAgbGVmdF9qb2luKGNlbnN1c19wb3BfZXN0XzIwMTgsDQogICAgICAgICAgICBieSA9IGMoInN0YXRlIiA9ICJzdGF0ZSIpKSAlPiUgDQogIG11dGF0ZShjb3ZpZF9wZXJfMTAwMDAgPSAoY2FzZXMvZXN0X3BvcF8yMDE4KSoxMDAwMCkNCg0KY292aWRfZGF5XzE1IDwtIGNvdmlkXzE5X3N0YXRlX3BvcHVsYXRpb25zICU+JSANCiAgIGZpbHRlcihkYXkoZGF0ZSkgPT0gMTUpDQogIA0KIA0KYGBgDQoNCg0KYGBge3J9DQp1c19wYW5kZW1pYyA8LSBjb3ZpZF9kYXlfMTUgJT4lIA0KICBnZ3Bsb3QoKSArDQogIGdlb21fbWFwKG1hcCA9IHN0YXRlc19tYXAsDQogICAgICAgICAgIGFlcyhtYXBfaWQgPSBzdGF0ZSwNCiAgICAgICAgICAgICAgIGZpbGwgPSBjb3ZpZF9wZXJfMTAwMDAsDQogICAgICAgICAgICAgICBncm91cCA9IGRhdGUpKSArICNmaW5kIG91dCB3aHkgZ3JvdXAgPSBkYXRlDQogIGV4cGFuZF9saW1pdHMoeCA9IHN0YXRlc19tYXAkbG9uZywgeSA9IHN0YXRlc19tYXAkbGF0KSArIA0KICBsYWJzKHRpdGxlID0gIkNPVklELTE5IENhc2VzIGluIHRoZSBVbml0ZWQgU3RhdGVzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJEYXRlOiB7Y2xvc2VzdF9zdGF0ZX0iLA0KICAgICAgIGZpbGwgPSAiQ2FzZXMgcGVyIDEwLDAwMCBwZW9wbGUiKSArIA0KICANCiAgdGhlbWVfbWFwKCkgKyANCiAgdGhlbWUobGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICsNCiAgdHJhbnNpdGlvbl9zdGF0ZXMoZGF0ZSwgdHJhbnNpdGlvbl9sZW5ndGggPSAwKQ0KICAgICAgICAgICAgICAgICAgICAgICNzdGF0ZV9sZW5ndGggPSAxMCkNCiAgDQphbmltYXRlKHVzX3BhbmRlbWljLCBuZnJhbWVzID0gMjAwLCBlbmRfcGF1c2UgPSAxMCkNCiAgDQphbmltX3NhdmUoIlE2cGFuZGVtaWN0aWRlLmdpZiIpDQogIA0KYGBgDQpgYGB7ciwgZmlnLmFsdD0iVGhpcyBnaWYgc2hvd3MgdGhlIFVuaXRlZCBTdGF0ZXMnIGN1bXVsYXRpdmUgQ09WSUQtMTkgY2FzZXMgcGVyIDEwLDAwMCByZXNpZGVudHMgYXMgaXQgY2hhbmdlcyBvdmVyIHRpbWUuIiB9DQoNCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJRNnBhbmRlbWljdGlkZS5naWYiKQ0KDQpgYGANCiMjIFlvdXIgZmlyc3QgYHNoaW55YCBhcHAgKGZvciBuZXh0IHdlZWshKQ0KDQogIDcuIFRoaXMgYXBwIHdpbGwgYWxzbyB1c2UgdGhlIENPVklEIGRhdGEuIE1ha2Ugc3VyZSB5b3UgbG9hZCB0aGF0IGRhdGEgYW5kIGFsbCB0aGUgbGlicmFyaWVzIHlvdSBuZWVkIGluIHRoZSBgYXBwLlJgIGZpbGUgeW91IGNyZWF0ZS4gWW91IHNob3VsZCBjcmVhdGUgYSBuZXcgcHJvamVjdCBmb3IgdGhlIGFwcCwgc2VwYXJhdGUgZnJvbSB0aGUgaG9tZXdvcmsgcHJvamVjdC4gQmVsb3csIHlvdSB3aWxsIHBvc3QgYSBsaW5rIHRvIHRoZSBhcHAgdGhhdCB5b3UgcHVibGlzaCBvbiBzaGlueWFwcHMuaW8uIFlvdSB3aWxsIGNyZWF0ZSBhbiBhcHAgdG8gY29tcGFyZSBzdGF0ZXMnIGRhaWx5IG51bWJlciBvZiBDT1ZJRCBjYXNlcyBwZXIgMTAwLDAwMCBvdmVyIHRpbWUuIFRoZSB4LWF4aXMgd2lsbCBiZSBkYXRlLiBZb3Ugd2lsbCBoYXZlIGFuIGlucHV0IGJveCB3aGVyZSB0aGUgdXNlciBjYW4gY2hvb3NlIHdoaWNoIHN0YXRlcyB0byBjb21wYXJlIChgc2VsZWN0SW5wdXQoKWApLCBhIHNsaWRlciB3aGVyZSB0aGUgdXNlciBjYW4gY2hvb3NlIHRoZSBkYXRlIHJhbmdlLCBhbmQgYSBzdWJtaXQgYnV0dG9uIHRvIGNsaWNrIG9uY2UgdGhlIHVzZXIgaGFzIGNob3NlbiBhbGwgc3RhdGVzIHRoZXkncmUgaW50ZXJlc3RlZCBpbiBjb21wYXJpbmcuIFRoZSBncmFwaCBzaG91bGQgZGlzcGxheSBhIGRpZmZlcmVudCBsaW5lIGZvciBlYWNoIHN0YXRlLCB3aXRoIGxhYmVscyBlaXRoZXIgb24gdGhlIGdyYXBoIG9yIGluIGEgbGVnZW5kLiBDb2xvciBjYW4gYmUgdXNlZCBpZiBuZWVkZWQuIA0KICANClB1dCB0aGUgbGluayB0byB5b3VyIGFwcCBoZXJlOiANCg0KX19JIGNvdWxkbid0IGdldCB0aGUgc2hpbnkgdG8gd29yayA6KF9fDQoNCkhlcmUgd2FzIG15IGNvZGU6IA0KbGlicmFyeSh0aWR5dmVyc2UpICAgICANCmxpYnJhcnkobHVicmlkYXRlKSAgICAgDQpsaWJyYXJ5KHNoaW55KSAgICAgICAgIA0KbGlicmFyeShiYWJ5bmFtZXMpDQpjb3ZpZDE5IDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbnl0aW1lcy9jb3ZpZC0xOS1kYXRhL21hc3Rlci91cy1zdGF0ZXMuY3N2IikNCg0KDQoNCnVpIDwtIGZsdWlkUGFnZSgiVW5pdGVkIFN0YXRlcycgZGFpbHkgbnVtYmVyIG9mIENPVklELTE5IGNhc2VzIHBlciAxMDAsMDAwIHBlb3BsZSBvdmVyIHRpbWUuIiwNCiAgc2xpZGVySW5wdXQoaW5wdXRJZCA9ICJkYXRlIiwgDQogICAgICAgICAgICAgIGxhYmVsID0gIkRhdGUgUmFuZ2UiLA0KICAgICAgICAgICAgICBtaW4gPSBhcy5EYXRlKG1pbihjb3ZpZDE5JGRhdGUpKSwgDQogICAgICAgICAgICAgIG1heCA9IGFzLkRhdGUobWF4KGNvdmlkMTkkZGF0ZSkpLCANCiAgICAgICAgICAgICAgdmFsdWUgPSBjKGFzLkRhdGUobWluKGNvdmlkMTkkZGF0ZSkpLCBhcy5EYXRlKG1heChjb3ZpZDE5JGRhdGUpKSkpLA0KICAjdGV4dElucHV0KCJuYW1lIiwgDQogICAgICAgICAgIyAgIk5hbWUiLCANCiAgICAgICAgICAjICB2YWx1ZSA9ICIiLCANCiAgICAgICAgICAgIyBwbGFjZWhvbGRlciA9ICJMaXNhIiksDQogIHNlbGVjdElucHV0KGlucHV0SWQgPSAic3RhdGUiLCANCiAgICAgICAgICAgICAgbGFiZWwgPSAiU3RhdGUiLCANCiAgICAgICAgICAgICAgY2hvaWNlcyA9IHVuaXF1ZShjb3ZpZDE5JHN0YXRlKSksDQogIHN1Ym1pdEJ1dHRvbih0ZXh0ID0gIkNvbXBhcmUgICAgICAgICAgICAgICAgICAgICAgICBTZWxlY3RlZCBTdGF0ZXMiKSwNCiAgcGxvdE91dHB1dChvdXRwdXRJZCA9ICJjb3ZpZHBsb3QiKQ0KKQ0KDQpzZXJ2ZXIgPC0gZnVuY3Rpb24oaW5wdXQsIG91dHB1dCkgew0KICANCiAgb3V0cHV0JGNvdmlkcGxvdCA8LSByZW5kZXJQbG90KHsgDQogICAgY292aWQxOSA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL255dGltZXMvY292aWQtMTktZGF0YS9tYXN0ZXIvdXMtc3RhdGVzLmNzdiIpDQogICAgDQogICAgY292aWQxOSA8LSBjb3ZpZDE5ICU+JSANCiAgICAgIGdyb3VwX2J5KHN0YXRlKSAlPiUgDQogICAgICBzdW1tYXJpc2UoY2FzZXMgPSBsYXN0KGNhc2VzKSkgJT4lIA0KICAgICAgbXV0YXRlKHN0YXRlID0gc3RyX3RvX2xvd2VyKGBzdGF0ZWApKQ0KICAgDQogICAgIGNlbnN1c19wb3BfZXN0XzIwMTggPC0gcmVhZF9jc3YoImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3MvNnR4d3YzYjRuZzdwZXBlL3VzX2NlbnN1c18yMDE4X3N0YXRlX3BvcF9lc3QuY3N2P2RsPTEiKSAlPiUgDQogICAgICBzZXBhcmF0ZShzdGF0ZSwgaW50byA9IGMoImRvdCIsInN0YXRlIiksIGV4dHJhID0gIm1lcmdlIikgJT4lIA0KICAgICAgc2VsZWN0KC1kb3QpICU+JSANCiAgICAgIG11dGF0ZShzdGF0ZSA9IHN0cl90b19sb3dlcihzdGF0ZSkpDQogICAgDQogICAgIGNvdmlkXzE5X3N0YXRlX3BvcHVsYXRpb25zIDwtIGNvdmlkMTlfc3RhdGVzICU+JSANCiAgICAgIGxlZnRfam9pbihjZW5zdXNfcG9wX2VzdF8yMDE4LA0KICAgICAgICAgICAgICAgIGJ5ID0gYygic3RhdGUiID0gInN0YXRlIikpDQogICAgIA0KICAgICBtb2RpZmllZF9jb3ZpZDE5IDwtIGNvdmlkXzE5X3N0YXRlX3BvcHVsYXRpb25zICU+JSANCiAgICAgICBtdXRhdGUoY292aWRfcHJvcCA9IGNhc2VzL2VzdF9wb3BfMjAxOCkgIA0KICAgICANCiAgICBtb2RpZmllZF9jb3ZpZDE5ICU+JSANCiAgICAgICNvbGQgY29kZQ0KICAgICAgI2ZpbHRlcihzdGF0ZSAlaW4lIHN0YXRlKSAlPiUgDQogICAgICAjbmV3IGNvZGUgLSB5b3UgZm9yZ290IGlucHV0JA0KICAgICAgZmlsdGVyKHN0YXRlICVpbiUgaW5wdXQkc3RhdGUpICU+JSANCiAgICAgIGdncGxvdCgpICsNCiAgICAgICNvbGQgY29kZQ0KICAgICAgI2dlb21fbGluZShhZXMoeCA9IGRhdGUsIHkgPSBjYXNlKSwgY29sb3IgPSBzdGF0ZSkgKw0KICAgICAgI25ldyBjb2RlIC0geW91IGhhZCBjYXNlIGluc3RlYWQgb2YgY2FzZXMgYW5kIGNvbG9yIHdhcyBvdXRzaWRlIG9mIGFlcygpDQogICAgICBnZW9tX2xpbmUoYWVzKHggPSBkYXRlLCB5ID0gY2FzZXMsIGNvbG9yID0gc3RhdGUpKSArDQogICAgICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gaW5wdXQkZGF0ZSkgKw0KICAgICAgdGhlbWVfbWluaW1hbCgpDQogIH0pDQp9DQoNCnNoaW55QXBwKHVpID0gdWksIHNlcnZlciA9IHNlcnZlcikNCiAgDQojIyBHaXRIdWIgbGluaw0KDQogIDguIEJlbG93LCBwcm92aWRlIGEgbGluayB0byB5b3VyIEdpdEh1YiByZXBvIHdpdGggdGhpcyBzZXQgb2YgV2Vla2x5IEV4ZXJjaXNlcy4gDQoNCltUaGUgYmVsZWFndWVyZWQgR2l0SHViIGxpbmtdKGh0dHBzOi8vZ2l0aHViLmNvbS9mb3JpYW5hL0lEU19FeGVyY2lzZV81LmdpdCkNCg0KKipESUQgWU9VIFJFTUVNQkVSIFRPIFVOQ09NTUVOVCBUSEUgT1BUSU9OUyBBVCBUSEUgVE9QPyoqDQo=